﻿using System;
using System.Collections.Generic; 
using System.Linq;
using System.Text;
using System.Collections;
using System.Runtime.Serialization;

namespace DataStruct
{

    public class Circle<T> : IEnumerable<T>, IEnumerable, ICollection<T>, ICollection, ISerializable
    {
        private Dictionary<T, CircleNode<T>> container = null;

        private CircleNode<T> head = null;

        public Circle()
        {
            container = new Dictionary<T, CircleNode<T>>();
        }

        public Circle(int capacity)
        {
            container = new Dictionary<T, CircleNode<T>>(capacity);
        }

        private CircleNode<T> CheckAndAdd(T value)
        {
            if (container.Keys.Contains(value))
            {
                throw new Exception("can't add same CircleNode");
            }
            CircleNode<T> node = new CircleNode<T>(this, value);
            container.Add(value, node);
            return node;
        }

        public CircleNode<T> GetCircleNode(T value)
        {
            if (container.Keys.Contains(value))
            {
                return container[value];
            }
            else
            {
                return null;
            }
        }

        public CircleNode<T> Add( T value )
        {
            return AddLast( value );
        }

        public CircleNode<T> AddFirst(T value)
        {
            if (head == null || container.Count == 0)
            {
                this.Clear();

                head = CheckAndAdd(value);
            }
            else if (container.Count == 1)
            {
                var node = CheckAndAdd(value);

                Twins(head, node);
                head = node;
            }
            else
            {
                head = AddAfter(head.Previous, value);
            }

            return head;
        }

        public CircleNode<T> AddLast(T value)
        {
            if (head == null || container.Count == 0)
            {
                return AddFirst(value);
            }
            else if (container.Count == 1)
            {
                var node = CheckAndAdd(value);

                Twins(head, node);
                return node;
            }
            else
            {
                return AddAfter(head.Previous, value);
            }
        }

        private void Twins(CircleNode<T> first, CircleNode<T> second)
        {
            first.Circle = second.Circle = this;
            first.Next = first.Previous = second;
            second.Next = second.Previous = first;
        }

        public CircleNode<T> AddAfter(CircleNode<T> node, T value)
        {
            if (node.Circle != this)
            {
                throw new Exception("CircleNode is not child of circle");
            }

            CircleNode<T> dummy = CheckAndAdd(value);
            CircleNode<T> swaptemp = node.Next;

            node.Next = swaptemp.Previous = dummy;
            dummy.Previous = node;
            dummy.Next = swaptemp;

            return dummy;
        }

        public CircleNode<T> AddBefore(CircleNode<T> node, T value)
        {
            if (node.Circle != this)
            {
                throw new Exception("CircleNode is not child of circle");
            }
            CircleNode<T> dummy = CheckAndAdd(value);
            CircleNode<T> swaptemp = node.Previous;

            node.Previous = swaptemp.Next = dummy;
            dummy.Next = node;
            dummy.Previous = swaptemp;

            return dummy;
        }

        public IEnumerator<T> GetEnumerator()
        {
            return container.Keys.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return container.Keys.GetEnumerator();
        }

        public int Count
        {
            get
            {
                return container.Count;
            }
        }

        public CircleNode<T> First
        {
            get
            {
                return head;
            }
        }

        void ICollection<T>.Add(T item)
        {
            AddLast(item);
        }

        public void Clear()
        {
            foreach (var item in container.Values)
            {
                item.Circle = null;
            }
            container.Clear();
            head = null;
        }

        bool ICollection<T>.Contains(T item)
        {
            return container.Keys.Contains(item);
        }

        bool Contains(CircleNode<T> item)
        {
            return container.Values.Contains(item);
        }

        void ICollection<T>.CopyTo(T[] array, int arrayIndex)
        {
            throw new NotImplementedException();
        }

        int ICollection<T>.Count
        {
            get { return container.Count; }
        }

        bool ICollection<T>.IsReadOnly
        {
            get { return false; }
        }

        bool ICollection<T>.Remove(T item)
        {
            throw new NotImplementedException();
        }

        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
            return container.Keys.GetEnumerator();
        }

        void ICollection.CopyTo(Array array, int index)
        {
            throw new NotImplementedException();
        }

        int ICollection.Count
        {
            get { return container.Count; }
        }

        bool ICollection.IsSynchronized
        {
            get { throw new NotImplementedException(); }
        }

        private object _SyncRoot = new object();
        object ICollection.SyncRoot
        {
            get { return _SyncRoot; }
        }

        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            List<T> list = new List<T>();
            foreach (CircleNode<T> t in container.Values)
            {
                list.Add(t.Value);
            }

            info.AddValue("Data", list, list.GetType());
        }

        protected Circle(SerializationInfo info, StreamingContext context)
        {
            List<T> list = info.GetValue("Data", typeof(List<T>)) as List<T>;

            foreach (T t in list)
            {
                container.Add(t, new CircleNode<T>(t));
            }
            if (list.Count > 0)
            {
                head = container[list[0]];
            }
        }

        public CircleNode<T> this[T item]
        {
            get
            {
                return container[item];
            }
            set
            {
                container[item] = value;
            }
        }


        //public T this[int index]
        //{
        //    get
        //    {
        //        // container.
        //    }
        //    set { /* set the specified index to value here */ }
        //}

    }
}


/*
      [Serializable]
      public struct Enumerator : IEnumerator<T>, IDisposable, IEnumerator, ISerializable, IDeserializationCallback
      {
          private Circle<T> list;
          private CircleNode<T> node;
          private T current;
          private int index;

          internal Enumerator(Circle<T> list)
          {
              this.list = list;
              this.node = null;
              this.current = default(T);
              this.index = 0;
          }

          public T IEnumerator<T>.Current
          {
              get { return this.current; }
          }

          void IDisposable.Dispose()
          {
          }

          public object IEnumerator.Current
          {
              get 
              {
                  if (this.index == 0)
                  {
                      throw new Exception("Please call MoveNext first.");
                  }
                  return this.current;
              }
          }

          public bool IEnumerator.MoveNext()
          {
              throw new NotImplementedException();
          }

          void IEnumerator.Reset()
          {
              this.current = default(T);
              this.index = 0;
          }

          void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
          {
              throw new NotImplementedException();
          }

          void IDeserializationCallback.OnDeserialization(object sender)
          {
              throw new NotImplementedException();
          }
      }*/